
/*
 *
 *  Copyright (C) 2012-2024
 *  All rights reserved, Designed By 杭州连线客信息技术有限公司
 *  注意：
 *  本软件为杭州连线客信息技术有限公司开发研制，未经购买不得使用
 *  购买后可获得全部源代码（禁止转卖、分享、上传到码云、github等开源平台）
 *  一经发现盗用、分享等行为，将追究法律责任，后果自负
 *
 */

package com.lxk.finance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lxk.finance.domain.AccUserWithdrawal;
import com.lxk.finance.domain.bo.AccUserAccountBo;
import com.lxk.finance.domain.bo.AccUserWithdrawalBo;
import com.lxk.finance.domain.bo.UserApplyBo;
import com.lxk.finance.domain.vo.AccUserWithdrawalVo;
import com.lxk.finance.enums.AccEnums;
import com.lxk.finance.mapper.AccUserWithdrawalMapper;
import com.lxk.finance.service.IAccUserAccountService;
import com.lxk.finance.service.IAccUserWithdrawalService;
import com.lxk.service.idgenerator.GeneratorType;
import com.lxk.service.idgenerator.IdGeneratorServiceApi;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidationUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 用户提现Service业务层处理
 *
 * @author LXK-RY5
 * @date 2024-10-09
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class AccUserWithdrawalServiceImpl implements IAccUserWithdrawalService {

    private final AccUserWithdrawalMapper baseMapper;
    private final IAccUserAccountService accUserAccountService;
    private final IdGeneratorServiceApi generatorServiceApi;


    /**
     * 审核
     *
     * @param id
     * @param dst
     * @param auditId 审核人员ID
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean audit(Long id, AccEnums.AuditStatus dst, Long auditId, String remark) {
        Assert.notNull(id, "id参数不能为空");
        AccUserWithdrawalVo accUserWithdrawalVo = this.queryById(id);
        String userType = accUserWithdrawalVo.getUserType();
        if (ObjectUtil.isNull(accUserWithdrawalVo)) {
            log.error("提现申请不存在:{}", id);
            throw new ServiceException("提现申请不存在");
        }
        Integer auditStatus = accUserWithdrawalVo.getAuditStatus();
        if (ObjectUtil.notEqual(auditStatus, AccEnums.AuditStatus.WAIT.getCode())) {
            String s = "提现申请状态未处于待审核状态，当前状态:" + AccEnums.AuditStatus.getByCode(auditStatus).getName();
            log.error(s);
            throw new ServiceException(s);
        }
        Boolean success = SpringUtils.getAopProxy(this).updateByAuditStatus(id, AccEnums.AuditStatus.WAIT.getCode(), dst.getCode(), auditId, remark);
        if (!success) {
            log.error("当前申请无法申请编号:{}", accUserWithdrawalVo.getWithdrawNo());
            throw new ServiceException("当前申请无法申请，请刷新申请列表");
        }
        // 如果审核成功，将由定时任务来完成后续打款动作

        // 审核失败，则把钱加回去
        if (dst == AccEnums.AuditStatus.FAIL || dst == AccEnums.AuditStatus.ROLLBACK) {
            accUserAccountService.addAccount(accUserWithdrawalVo.getUserId(), userType, accUserWithdrawalVo.getWithdrawNo(), accUserWithdrawalVo.getSrcPrice(), AccUserAccountBo.AccountType.getByCode(accUserWithdrawalVo.getAccountType()), "审核失败，退款到余额");
        }
        return success;
    }

    /**
     * 更新审核状态
     *
     * @param id
     * @param srcStatus
     * @param dstStatus
     * @param auditId
     * @param remark
     * @return
     */
    @Override
    public Boolean updateByAuditStatus(Long id, Integer srcStatus, Integer dstStatus, Long auditId, String remark) {
        Assert.notNull(id);
        Assert.notNull(srcStatus);
        Assert.notNull(dstStatus);
        AccUserWithdrawal update = new AccUserWithdrawal();
        update.setAuditStatus(dstStatus);
        update.setAuditTime(new Date());
        update.setAuditId(auditId);
        update.setRefusalReason(remark);
        update.setPlatformRemark(remark);
        int i = baseMapper.update(update, Wrappers.<AccUserWithdrawal>lambdaUpdate()
            .eq(AccUserWithdrawal::getId, id)
            .eq(AccUserWithdrawal::getAuditStatus, srcStatus)
        );
        return i == 1;
    }

    /**
     * 针对申请提现-支持微信和银行卡
     * <p>
     * 1.用户余额是否足够
     * 2.获取手续费配置-暂未实现
     * 3.申请成功后，需要扣除用户余额
     *
     * @param apply
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean applyWithdrawal(UserApplyBo apply) {
        AccEnums.Type type = apply.getType();
        if (type == AccEnums.Type.BANK) {
            ValidationUtils.validate(apply, UserApplyBo.Common.class, UserApplyBo.Bank.class);
        } else if (type == AccEnums.Type.WX) {
            ValidationUtils.validate(apply, UserApplyBo.Common.class, UserApplyBo.Wx.class);
        } else if (type == AccEnums.Type.ALI) {
            ValidationUtils.validate(apply, UserApplyBo.ALI.class, UserApplyBo.Common.class);
        } else {
            ValidationUtils.validate(apply, UserApplyBo.Common.class);
        }
        String generatorSn = generatorServiceApi.generatorSn(GeneratorType.WITHDRAW_ORDER);
        Long userId = apply.getUserId();
        BigDecimal amount = apply.getAmount();
        BigDecimal serviceFeeRate = apply.getServiceFeeRate();
        String realName = apply.getRealName();
        String remark = apply.getRemark();
        AccUserAccountBo.AccountType accountType = apply.getAccountType();
        String userType = apply.getUserType();
        String bankCardNo = apply.getBankCardNo();
        String bankName = apply.getBankName();
        Boolean reduced = accUserAccountService.reduceAccount(apply.getUserId(), userType, generatorSn, apply.getAmount(), apply.getAccountType(), "用户申请提现");
        if (!reduced) {
            log.error("申请提现,用户余额不足userId:{},amount:{}", userId, amount);
            throw new ServiceException("用户余额不足");
        }
        AccUserWithdrawalBo add = new AccUserWithdrawalBo();
        add.setUserId(userId);
        add.setServiceFee(amount.multiply(serviceFeeRate));
        add.setServiceFeeRate(serviceFeeRate);
        add.setSrcPrice(amount);
        add.setActPrice(add.getSrcPrice().subtract(add.getServiceFee()));
        add.setWithdrawNo(generatorSn);
        add.setArrivalStatus(AccEnums.ArrivalStatus.NOT.getCode());
        add.setAuditStatus(AccEnums.AuditStatus.WAIT.getCode());
        add.setType(type.getCode());
        add.setUserRemark(remark);
        add.setAccountType(accountType.getCode());
        add.setUserType(userType);
        add.setBankCardNo(bankCardNo);
        add.setBankName(bankName);
        add.setRealName(realName);
        return SpringUtils.getAopProxy(this).insertByBo(add);
    }

    /**
     * 针对申请提现
     * <p>
     * 1.用户余额是否足够
     * 2.获取手续费配置-暂未实现
     * 3.申请成功后，需要扣除用户余额
     *
     * @param userId
     * @param amount
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean applyForWx(Long userId, String userType, BigDecimal amount, BigDecimal serviceFeeRate,
                              AccUserAccountBo.AccountType accountType,
                              String wxOpenId, String remark) {
        Assert.notNull(userId);
        Assert.notNull(userType);
        String generatorSn = generatorServiceApi.generatorSn(GeneratorType.WITHDRAW_ORDER);
        Boolean reduced = accUserAccountService.reduceAccount(userId, userType, generatorSn, amount, accountType, "用户申请提现");
        if (!reduced) {
            log.error("申请提现,用户余额不足userId:{},amount:{}", userId, amount);
            throw new ServiceException("用户余额不足");
        }
        AccUserWithdrawalBo add = new AccUserWithdrawalBo();
        add.setUserId(userId);
        add.setServiceFee(amount.multiply(serviceFeeRate));
        add.setServiceFeeRate(serviceFeeRate);
        add.setSrcPrice(amount);
        add.setActPrice(add.getSrcPrice().subtract(add.getServiceFee()));
        add.setWithdrawNo(generatorSn);
        add.setOpenId(wxOpenId);
        add.setArrivalStatus(AccEnums.ArrivalStatus.NOT.getCode());
        add.setAuditStatus(AccEnums.AuditStatus.WAIT.getCode());
        add.setType(AccEnums.Type.WX.getCode());
        add.setUserRemark(remark);
        add.setAccountType(accountType.getCode());
        add.setUserType(userType);
        return SpringUtils.getAopProxy(this).insertByBo(add);
    }

    /**
     * 查询IdList用户提现Map
     */
    @Override
    public Map<Long, AccUserWithdrawalVo> queryMapByIdList(List<Long> idList) {
        return this.queryByIdList(idList).stream().collect(Collectors.toMap(AccUserWithdrawalVo::getId, v -> v));
    }

    /**
     * 查询用户提现Map
     */
    @Override
    public Map<Long, AccUserWithdrawalVo> queryMap(AccUserWithdrawalBo bo) {
        return this.queryList(bo).stream().collect(Collectors.toMap(AccUserWithdrawalVo::getId, v -> v));
    }

    /**
     * 查询用户提现IdList
     */
    @Override
    public List<AccUserWithdrawalVo> queryByIdList(List<Long> idList) {
        if (CollUtil.isEmpty(idList)) {
            return CollUtil.newArrayList();
        }
        LambdaQueryWrapper<AccUserWithdrawal> lqw = Wrappers.lambdaQuery();
        lqw.in(AccUserWithdrawal::getId, idList);
        return baseMapper.selectVoList(lqw);
    }

    /**
     * 查询用户提现
     *
     * @param id 主键
     * @return 用户提现
     */
    @Override
    public AccUserWithdrawalVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询用户提现列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 用户提现分页列表
     */
    @Override
    public TableDataInfo<AccUserWithdrawalVo> queryPageList(AccUserWithdrawalBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<AccUserWithdrawal> lqw = buildQueryWrapper(bo);
        Page<AccUserWithdrawalVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的用户提现列表
     *
     * @param bo 查询条件
     * @return 用户提现列表
     */
    @Override
    public List<AccUserWithdrawalVo> queryList(AccUserWithdrawalBo bo) {
        LambdaQueryWrapper<AccUserWithdrawal> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<AccUserWithdrawal> buildQueryWrapper(AccUserWithdrawalBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<AccUserWithdrawal> lqw = Wrappers.lambdaQuery();
        lqw.orderByDesc(AccUserWithdrawal::getId);
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            AccUserWithdrawal::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.eq(StringUtils.isNotBlank(bo.getWithdrawNo()), AccUserWithdrawal::getWithdrawNo, bo.getWithdrawNo());
        lqw.eq(bo.getUserId() != null, AccUserWithdrawal::getUserId, bo.getUserId());
        lqw.eq(bo.getSrcPrice() != null, AccUserWithdrawal::getSrcPrice, bo.getSrcPrice());
        lqw.eq(bo.getActPrice() != null, AccUserWithdrawal::getActPrice, bo.getActPrice());
        lqw.eq(bo.getServiceFee() != null, AccUserWithdrawal::getServiceFee, bo.getServiceFee());
        lqw.eq(bo.getServiceFeeRate() != null, AccUserWithdrawal::getServiceFeeRate, bo.getServiceFeeRate());
        lqw.eq(StringUtils.isNotBlank(bo.getRefusalReason()), AccUserWithdrawal::getRefusalReason, bo.getRefusalReason());
        lqw.eq(bo.getAuditId() != null, AccUserWithdrawal::getAuditId, bo.getAuditId());
        lqw.between(params.get("beginAuditTime") != null && params.get("endAuditTime") != null,
            AccUserWithdrawal::getAuditTime, params.get("beginAuditTime"), params.get("endAuditTime"));
        lqw.eq(bo.getAuditStatus() != null, AccUserWithdrawal::getAuditStatus, bo.getAuditStatus());
        lqw.eq(bo.getArrivalStatus() != null, AccUserWithdrawal::getArrivalStatus, bo.getArrivalStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getPlatformRemark()), AccUserWithdrawal::getPlatformRemark, bo.getPlatformRemark());
        lqw.eq(StringUtils.isNotBlank(bo.getUserRemark()), AccUserWithdrawal::getUserRemark, bo.getUserRemark());
        lqw.eq(StringUtils.isNotBlank(bo.getBankCardNo()), AccUserWithdrawal::getBankCardNo, bo.getBankCardNo());
        lqw.like(StringUtils.isNotBlank(bo.getBankName()), AccUserWithdrawal::getBankName, bo.getBankName());
        lqw.like(StringUtils.isNotBlank(bo.getRealName()), AccUserWithdrawal::getRealName, bo.getRealName());
        lqw.eq(StringUtils.isNotBlank(bo.getType()), AccUserWithdrawal::getType, bo.getType());
        return lqw;
    }

    /**
     * 新增用户提现
     *
     * @param bo 用户提现
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(AccUserWithdrawalBo bo) {
        AccUserWithdrawal add = MapstructUtils.convert(bo, AccUserWithdrawal.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改用户提现
     *
     * @param bo 用户提现
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(AccUserWithdrawalBo bo) {
        AccUserWithdrawal update = MapstructUtils.convert(bo, AccUserWithdrawal.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(AccUserWithdrawal entity) {
    }

    /**
     * 校验并批量删除用户提现信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
